﻿// Copyright (C) 2009 - 2013 EvTech Project
//
// This program is free software: you can redistribute it and/or modify
// it under the +terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Piirto
{
    class RouteTool
    {
        protected Color visibleRouteColor;
        protected Color visitedRouteColor;
        protected Color unVisitedRouteColor;
        protected int penWidth;
        protected int borderWidth;
        private List<Point> routepoints;
        private int visitedpoints, currentPoint, visibleStartPoint, visibleEndPoint;

        public RouteTool(Color visibleRouteColor, Color visitedRouteColor, 
            Color unVisitedRouteColor, int borderWidth, int penWidth)
        {
            this.visibleRouteColor = visibleRouteColor;
            this.visitedRouteColor = visitedRouteColor;
            this.unVisitedRouteColor = unVisitedRouteColor;
            this.borderWidth = borderWidth;
            this.penWidth = penWidth;
            routepoints = new List<Point>();
            visitedpoints = 0;
            currentPoint = 0;
            visibleStartPoint = 0;
            visibleEndPoint = 0;
        }

        public int getPenWidth()
        {
            return penWidth;
        }

        public void setPenWidth(int width)
        {
            penWidth = width;
        }

        public int getBorderWidth()
        {
            return borderWidth;
        }

        public void setBorderWidth(int width)
        {
            this.borderWidth = width;
        }


        public bool newPoint(int x, int y)
        {
            routepoints.Add(new Point(x, y));
            return true;
        }

        public void visitPoint(int x, int y)
        {
            currentPoint = getNearestPointIndex(new Point(x, y));
            setCurrentPoint(currentPoint);
        }

        public int getCurrentPointIndex()
        {
            return currentPoint;
        }

        protected int getNearestPointIndex(Point p)
        {
            int nearestPointIndex = currentPoint;
            int shortestDistance = getDistance(p, routepoints[currentPoint]);
            for (int i = 1; currentPoint + i < routepoints.Count; i++)
            {
                int distance = getDistance(p, routepoints[currentPoint + i]);
                if (shortestDistance > distance)
                {
                    nearestPointIndex = currentPoint + i;
                    shortestDistance = distance;
                }
                else if (i<2 && currentPoint + 2 < routepoints.Count && shortestDistance > getDistance(p, routepoints[currentPoint + 2]))
                {
                    nearestPointIndex = currentPoint + 2;
                    shortestDistance = distance;
                    i = 2;
                }
                else
                    break;
            }
            /*
            for (int i = 1; currentPoint - i >= 0; i++)
            {
                int distance = getDistance(p, routepoints[currentPoint - i]);
                if (shortestDistance > distance)
                {
                    shortestDistance = distance;
                    nearestPointIndex = currentPoint - i;
                }
                else
                    break;
            }
             * */
            return nearestPointIndex;
        }

        public Point getCurrentPoint()
        {
            return routepoints[currentPoint];
        }

        protected void setCurrentPoint(int locIndex)
        {
            int distance = 0;
            currentPoint = locIndex;
            visibleStartPoint = currentPoint - 1;
            for (int i = 1; currentPoint - i >= 0; i++)
            {
                distance = distance + getDistance(routepoints[currentPoint - i], routepoints[currentPoint - i + 1]);
                if (distance < 2 * penWidth)
                {
                    visibleStartPoint = currentPoint - i;
                }
            }
            if (visibleStartPoint < 0)
                visibleStartPoint = 0;

            visibleEndPoint = currentPoint + 1;
            distance = 0;
            for (int i = 1; currentPoint + i < routepoints.Count; i++)
            {
                distance = distance + getDistance(routepoints[currentPoint + i], routepoints[currentPoint + i - 1]);
                if (distance < 2 * penWidth)
                {
                    visibleEndPoint = currentPoint + i;
                }
            }
            if (visibleEndPoint >= routepoints.Count)
                visibleEndPoint = routepoints.Count - 1;
            if (visibleEndPoint < 0)
                visibleEndPoint = 0;
            if (currentPoint >= visitedpoints)
            {
                visitedpoints = currentPoint + 1;
            }
        }

        protected int getDistance(Point p1, Point p2)
        {
            int result = 0;
            double part1 = Math.Pow((p2.X - p1.X), 2);
            double part2 = Math.Pow((p2.Y - p1.Y), 2);
            double underRadical = part1 + part2;
            result = (int)Math.Sqrt(underRadical);
            return result;
        }

        public void Paint(Graphics g)
        {
            if (routepoints.Count < 1)
            {
                return;
            }
            Pen visibleRoutePen = new Pen(visibleRouteColor, penWidth);
            Pen visitedRoutePen = new Pen(visitedRouteColor, penWidth);
            Pen unVisitedRoutePen = new Pen(unVisitedRouteColor, penWidth);
            for (int i = routepoints.Count - 1; i > 0; i--)
            {
                if (i < visitedpoints)
                {
                    g.FillEllipse(new SolidBrush(visitedRouteColor), routepoints[i].X - penWidth / 2,
                        routepoints[i].Y - penWidth / 2, penWidth, penWidth);
                    g.DrawLine(visitedRoutePen, routepoints[i - 1].X, routepoints[i - 1].Y, routepoints[i].X, routepoints[i].Y);
                }
                else
                {
                    g.FillEllipse(new SolidBrush(unVisitedRouteColor), routepoints[i].X - penWidth / 2,
                        routepoints[i].Y - penWidth / 2, penWidth, penWidth);
                    g.DrawLine(unVisitedRoutePen, routepoints[i - 1].X, routepoints[i - 1].Y, routepoints[i].X, routepoints[i].Y);
                }
            }
            if (routepoints.Count > 0)
            {
                g.FillEllipse(new SolidBrush(visitedRouteColor), routepoints[0].X - penWidth / 2,
                    routepoints[0].Y - penWidth / 2, penWidth, penWidth);

                for (int i = visibleStartPoint; i < visibleEndPoint; i++)
                {
                    g.FillEllipse(new SolidBrush(visibleRouteColor), routepoints[i].X - penWidth / 2,
                        routepoints[i].Y - penWidth / 2, penWidth, penWidth);
                    g.DrawLine(visibleRoutePen, routepoints[i].X, routepoints[i].Y, 
                        routepoints[i + 1].X, routepoints[i + 1].Y);
                }
                g.FillEllipse(new SolidBrush(visibleRouteColor), routepoints[visibleEndPoint].X - penWidth / 2,
                    routepoints[visibleEndPoint].Y - penWidth / 2, penWidth, penWidth);
            }
        }

        public void clear()
        {
            routepoints.Clear();
            visitedpoints = 0;
            setCurrentPoint(0);
        }

        internal List<Point> getRoutePoints()
        {
            return routepoints;
        }

        internal void resetVisitedPoints()
        {
            visitedpoints = 0;
            setCurrentPoint(0);
        }

        internal Color getRouteColor()
        {
            return visibleRouteColor;
        }

        internal bool isAllPointsVisited()
        {
            return (visitedpoints >= routepoints.Count - 1);
        }

        internal int packRoute()
        {
            int currentIndex = 1;
            while (currentIndex < routepoints.Count)
            {
                int distance = getDistance(routepoints[currentIndex-1], routepoints[currentIndex]);
                if (distance < penWidth / 2)
                {
                    routepoints.RemoveAt(currentIndex);
                }
                else
                {
                    currentIndex++;
                }
            }
            return routepoints.Count;
        }

        internal bool testRoute()
        {
            for (int i = 1; i < routepoints.Count; i++)
            {
                int distance = getDistance(routepoints[0], routepoints[i]);
                if (distance > penWidth * 2)
                {
                    return true;
                }
            }
            return false;
        }
    }
}
